Skip to content

feat(auth): add web (authorization-code) auth mode#188

Open
ICantThinkOfAName-tech wants to merge 1 commit into
XeroAPI:mainfrom
ICantThinkOfAName-tech:feat/web-auth-mode
Open

feat(auth): add web (authorization-code) auth mode#188
ICantThinkOfAName-tech wants to merge 1 commit into
XeroAPI:mainfrom
ICantThinkOfAName-tech:feat/web-auth-mode

Conversation

@ICantThinkOfAName-tech

Copy link
Copy Markdown

Summary

Adds a third authentication mode — Web (Authorization Code) — alongside the existing Custom Connections and Bearer Token modes. It performs the standard OAuth2 authorization-code flow and persists a refresh token, so the server can run headless and auto-refresh.

Motivation

Standard OAuth2 (auth-code) Xero apps reject the client_credentials grant used by Custom Connections (invalid_scope / "Client credentials scope validation failed"), and Custom Connections don't support refresh-backed, multi-tenant access. Today the only non-custom option is a static bearer token that expires. This adds a first-class, refreshable web-auth path.

What's added

  • WebAuthXeroClient (src/clients/xero-client.ts): loads the persisted tokenset, refreshes it on expiry, and resolves the tenant (XERO_TENANT_ID env → persisted value → /connections). Surfaces a clear, actionable error pointing to npm run auth when no tokenset exists or a refresh fails.
  • src/clients/token-store.ts: small JSON tokenset persistence helper at XERO_TOKENSET_PATH (default <repo>/.xero-tokenset.json, written 0600).
  • src/auth.ts + npm run auth: one-time consent CLI — builds the consent URL, opens it, runs a short-lived local callback server to capture the code, exchanges it, resolves the tenant, and saves the tokenset.
  • XERO_AUTH_MODE selector (web | custom | bearer). When unset, behaviour is unchanged (bearer if XERO_CLIENT_BEARER_TOKEN is set, otherwise custom connections).
  • README section documenting setup; .gitignore entry for .xero-tokenset.json.

Reuses xero-node's built-in OAuth (initialize/buildConsentUrl/apiCallback/refreshToken) — no new dependencies.

New env vars

Var Purpose Default
XERO_AUTH_MODE web selects this mode unset → custom/bearer (unchanged)
XERO_REDIRECT_URI OAuth redirect (must be registered in the Xero app) http://localhost:5000/callback
XERO_SCOPES space-separated; include offline_access granular accounting scopes
XERO_TOKENSET_PATH tokenset file location <repo>/.xero-tokenset.json
XERO_TENANT_ID pin a specific org first connected tenant

Backward compatibility

Fully additive. With XERO_AUTH_MODE unset the selection logic is identical to before.

Testing

Built clean and ran npm run auth end-to-end against a real Xero organisation (consent → tokenset saved → tenant resolved), then exercised list-invoices / list-contacts with automatic token refresh on expiry.

Adds a third auth mode alongside Custom Connections and Bearer Token:
standard OAuth2 Authorization Code flow with a refresh token persisted to
disk, selected via XERO_AUTH_MODE=web. Needed because standard auth-code
Xero apps reject the client_credentials grant (invalid_scope), and to
support refresh-backed multi-tenant access.

- WebAuthXeroClient: loads tokenset, refreshes on expiry, resolves tenant.
- token-store.ts: JSON tokenset persistence (XERO_TOKENSET_PATH, 0600).
- auth.ts: one-time consent CLI (`npm run auth`) — consent URL + local
  callback server, exchanges + saves the tokenset.
- XERO_AUTH_MODE switch (web|custom|bearer); unset preserves prior behaviour.
- README: web-auth setup; .gitignore: .xero-tokenset.json.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant